/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.to.aboomy.bannersample.indicator;

import com.to.aboomy.banner.Indicator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

/**
 * 渐变指示器
 *
 * @since 2021-08-07
 */
public class FillIndicatorView extends Component implements Indicator,
        Component.DrawTask, Component.EstimateSizeListener {
    private static final int NUM_2 = 2;
    private static final int NUM_3 = 3;
    private static final int NUM_10 = 10;
    private static final int NUM_30 = 30;
    private static final int NUM_100 = 100;
    private static final int NUM_200 = 200;
    private static final float NUM_F_0_5 = 0.5f;
    private static final float NUM_F_2 = 2.0f;
    private static final float NUM_F_6 = 6f;
    private Context context;
    private RectFloat rectF = null;
    /*--------------- 核心控制点大小距离参数 ---------------*/
    private float indicatorRadius;
    private float indicatorRatio = 1.0f;
    private float indicatorSelectedRadius;
    private float indicatorSelectedRatio = 1.0f;
    private float indicatorSpacing;
    private Paint slidPaint;
    private Paint wpaint;
    private Paint tpaint;
    private Paint indicatorPaint = null;
    private int displayWith = 0;
    private int pagerCount = 0;
    private int selectedPage = 0;
    private float offset;
    private Color selectedColor = Color.RED;
    private Color unColor = Color.GRAY;
    private boolean isSquare;
    private boolean isFirst;

    /**
     * 是否开启自动循环
     */
    private boolean isAutoLoop = false;
    private AnimatorValue animatorValue;
    /**
     * 控制在banner中的位置
     */
    private DependentLayout.LayoutConfig params;

    /**
     * 构造
     *
     * @param context
     */
    public FillIndicatorView(Context context) {
        this(context, null, null);
        this.context = context;
    }

    /**
     * 构造
     *
     * @param context
     * @param attrSet
     * @param styleName
     */
    public FillIndicatorView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        isSquare = true;
        isFirst = true;
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        displayWith = display.getAttributes().width;
        indicatorRadius = AttrHelper.fp2px(NUM_F_6, context);
        indicatorSelectedRadius = AttrHelper.fp2px(NUM_F_6, context);
        indicatorSpacing = AttrHelper.fp2px(NUM_10, context);

        // 设置测量组件的监听器
        rectF = new RectFloat();
        indicatorPaint = new Paint();
        indicatorPaint.setStrokeWidth(NUM_3);
        indicatorPaint.setStyle(Paint.Style.STROKE_STYLE);
        indicatorPaint.setColor(Color.GRAY);

        slidPaint = new Paint();
        slidPaint.setStyle(Paint.Style.FILLANDSTROKE_STYLE);
        slidPaint.setColor(selectedColor);

        wpaint = new Paint();
        wpaint.setStyle(Paint.Style.FILL_STYLE);
        wpaint.setColor(Color.WHITE);

        tpaint = new Paint();
        tpaint.setStyle(Paint.Style.FILL_STYLE);
        tpaint.setColor(Color.TRANSPARENT);
        addDrawTask(this);
        setEstimateSizeListener(this);
    }

    @Override
    public void initIndicatorCount(int pagerCount1) {
        this.pagerCount = pagerCount1;
        setVisibility(pagerCount > 1 ? VISIBLE : HIDE);
        postLayout();
    }

    @Override
    public Component getView() {
        return this;
    }

    @Override
    public DependentLayout.LayoutConfig getParams() {
        if (params == null) {
            params = new DependentLayout.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_CONTENT, NUM_100);
            params.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_BOTTOM);
            params.setMarginTop(NUM_30);
        }
        return params;
    }


    @Override
    public void setAutoStau(Boolean isAuto) {
        this.isAutoLoop = isAuto;
    }

    /**
     * 是否开启循环
     *
     * @return boolean
     */
    public boolean isAutoLoop() {
        return isAutoLoop;
    }

    @Override
    public void onPageSliding(int position, float positionOffset, int positionOffsetPixels) {
        if (positionOffsetPixels > 0) {
            isSquare = true;
        } else {
            isSquare = false;
        }
    }

    @Override
    public void onPageSlideStateChanged(int i) {
    }

    @Override
    public void onPageChosen(int position) {
        selectedPage = position;
        offset = 0;
        if (animatorValue == null) {
            animatorValue = new AnimatorValue();
        }
        animatorValue.setDuration(NUM_200);
        animatorValue.setValueUpdateListener((animatorValue1, v1) -> {
            offset = v1;
            invalidate();
        });
        animatorValue.start();
        postLayout();
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        indicatorPaint.setAntiAlias(true);
        if (pagerCount == 0) {
            return;
        }
        float midY = NUM_30 / NUM_F_2 + NUM_F_0_5;
        drawCircle(canvas, midY);
    }

    @Override
    public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
        int width = Component.EstimateSpec.getSize(widthEstimateConfig);
        int height = Component.EstimateSpec.getSize(heightEstimateConfig);
        setEstimatedSize(
                Component.EstimateSpec.getChildSizeWithMode(width, width, EstimateSpec.NOT_EXCEED),
                Component.EstimateSpec.getChildSizeWithMode(height, height, Component.EstimateSpec.NOT_EXCEED)
        );
        return true;
    }

    /**
     * 画滚动轨迹的圆
     *
     * @param canvas canvas
     * @param midY midY
     */
    private void drawCircle(Canvas canvas, float midY) {
        indicatorPaint.setStrokeWidth(NUM_3);
        if (isFirst) {
            isFirst = false;

            // 画默认圆
            drawPagerCountCircle(canvas, midY);
            drawRedCircle(canvas, midY);
        } else {
            // 画默认圆
            drawPagerCountCircle(canvas, midY);
            drawPagerCurrentCircle(canvas, midY); // 画现在的圆
            drawPagerLastCircle(canvas, midY); // 画上一个的圆
        }
    }

    // 画上一个的圆
    private void drawPagerLastCircle(Canvas canvas, float midY) {
        int currentpage = getLastPageIndex(selectedPage);
        float startCx = indicatorStartX(currentpage);
        float ratioIndicatorRadius = getRatioRadius();
        float left = startCx - ratioIndicatorRadius;
        float right = startCx + ratioIndicatorRadius;
        if (offset == 1.0) {
            // 画灰色边框
            indicatorPaint.setStrokeWidth(NUM_F_0_5);
            canvas.drawCircle(left + (right - left) / NUM_2,
                    getHeight() / NUM_F_2, (right - left) / NUM_2, indicatorPaint);

            // 画灰色内部
            canvas.drawCircle(left + (right - left) / NUM_2,
                    getHeight() / NUM_F_2, (right - left) / NUM_2, tpaint);
        } else {
            float radius = (right - left) / NUM_2 + 1;

            // 红色的
            canvas.drawCircle(left + (right - left) / NUM_2,
                    getHeight() / NUM_F_2, radius, slidPaint);

            // b色的
            canvas.drawCircle(left + (right - left) / NUM_2,
                    getHeight() / NUM_F_2, radius * offset, wpaint);
        }
    }

    // 画现在的圆
    private void drawPagerCurrentCircle(Canvas canvas, float midY) {
        float startCx = indicatorStartX(selectedPage);
        float ratioIndicatorRadius = getRatioRadius();
        float left = startCx - ratioIndicatorRadius;
        float top = midY - indicatorRadius;
        float right = startCx + ratioIndicatorRadius;
        float bottom = midY + indicatorRadius;
        rectF = new RectFloat(left, top, right, bottom);
        rectF.modify(left, top, right, bottom);

        // 红色的
        canvas.drawCircle(left + (right - left) / NUM_2, getHeight() / NUM_F_2,
                (right - left) / NUM_2 + NUM_2, slidPaint);
        float radius = (right - left) / NUM_2 + NUM_2;

        // 白色的
        canvas.drawCircle(left + (right - left) / NUM_2, getHeight() / NUM_F_2,
                radius * (1 - offset), wpaint);
    }

    private void drawRedCircle(Canvas canvas, float midY) {
        float startCx = indicatorStartX(selectedPage);
        float ratioIndicatorRadius = getRatioRadius();
        float left = startCx - ratioIndicatorRadius;
        float top = midY - indicatorRadius;
        float right = startCx + ratioIndicatorRadius;
        float bottom = midY + indicatorRadius;
        rectF = new RectFloat(left, top, right, bottom);
        rectF.modify(left, top, right, bottom);

        // 红色的
        canvas.drawCircle(left + (right - left) / NUM_2, getHeight() / NUM_F_2,
                (right - left) / NUM_2 + NUM_2, slidPaint);
    }

    /**
     * 画基础的圆
     *
     * @param canvas
     * @param midY
     */
    private void drawPagerCountCircle(Canvas canvas, float midY) {
        indicatorPaint.setColor(unColor);
        for (int i1 = 0; i1 < pagerCount; i1++) {
            float startCx = indicatorStartX(i1);
            float ratioIndicatorRadius = getRatioRadius();
            float left = startCx - ratioIndicatorRadius;
            float top = midY - indicatorRadius;
            float right = startCx + ratioIndicatorRadius;
            float bottom = midY + indicatorRadius;
            rectF = new RectFloat(left, top, right, bottom);
            rectF.modify(left, top, right, bottom);
            canvas.drawCircle(left + (right - left) / NUM_2, getHeight() / NUM_F_2,
                    (right - left) / NUM_2 + NUM_2, indicatorPaint);
        }
    }

    /**
     * 计算下一个索引值
     *
     * @param selectedPage1
     * @return int
     */
    private int getLastPageIndex(int selectedPage1) {
        int lastPageIndex = 0;
        if (isSquare) {
            int temp = 0;
            if (temp == selectedPage1) {
                lastPageIndex = pagerCount - 1;
            } else {
                lastPageIndex = selectedPage1 - 1;
            }
        } else {
            int temp2 = pagerCount - 1;
            if (temp2 == selectedPage1) {
                lastPageIndex = 0;
            } else {
                lastPageIndex = selectedPage1 + 1;
            }
        }
        return lastPageIndex;
    }

    private float indicatorStartX(int index) {
        float ratioRadius = getRatioRadius();
        float ratioSelectedRadius = getRatioSelectedRadius();
        float ratioIndicatorRadius = Math.max(ratioRadius, ratioSelectedRadius);
        float centerSpacing = ratioIndicatorRadius * NUM_F_2 + indicatorSpacing - NUM_10;
        float ssx = (displayWith - centerSpacing * pagerCount) / NUM_2;
        float centerX = ratioIndicatorRadius + getPaddingLeft() + centerSpacing * index;
        /*
           为了适配INDICATOR_DASH样式， measure 中默认多增加了 ratioIndicatorRadius - ratioRadius 的高度和宽度
           除了INDICATOR_DASH样式下，其他样式需要增加indicatorSelectedRadius一半的距离，让位置居中。
         */
        return centerX + ((ratioIndicatorRadius - ratioRadius) / NUM_2) + ssx;
    }

    /**
     * getRatioRadius
     *
     * @return float
     */
    private float getRatioRadius() {
        return indicatorRadius * indicatorRatio;
    }

    /**
     * getRatioSelectedRadius
     *
     * @return float
     */
    private float getRatioSelectedRadius() {
        return indicatorSelectedRadius * indicatorSelectedRatio;
    }

    /**
     * 设置选中的圆角，没有设置，默认和indicatorRadius值一致
     *
     * @param indicatorSelectedRadius1 单位dp
     * @return Indicator
     */
    public Indicator setIndicatorSelectedRadius(float indicatorSelectedRadius1) {
        this.indicatorSelectedRadius = AttrHelper.vp2px(indicatorSelectedRadius1, context);
        return this;
    }

    /**
     * setIndicatorColor
     *
     * @param indicatorColor
     * @return Indicator
     */
    public Indicator setIndicatorColor(Color indicatorColor) {
        this.unColor = indicatorColor;
        return this;
    }

    /**
     * setIndicatorSelectorColor
     *
     * @param indicatorSelectorColor
     * @return Indicator
     */
    public Indicator setIndicatorSelectorColor(Color indicatorSelectorColor) {
        this.selectedColor = indicatorSelectorColor;
        return this;
    }
}
